﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using ChatData;
using System.Diagnostics;
////using ClientServerChat;

namespace ChatServerOptions
{
    /// <summary>
    /// Handles Client Requests
    /// </summary>
    public class TheServer
    {
        ActiveUserStore<Record> activeusers = new ActiveUserStore<Record>();
        UserStore<StoreRecord>users = new UserStore<StoreRecord>();
        StreamStore thestream = new StreamStore();
        DMStore DMstream = new DMStore();
        FileStore fileStore = new FileStore();

        EventLog eventlog = new EventLog();

        public TheServer()
        {
            // service must run under the same user account as the AppData
            if (!EventLog.SourceExists("ChatServer"))
            {
                EventLog.CreateEventSource("ChatServer", "Application");              
            }
            eventlog.Source = "ChatServer";
            eventlog.WriteEntry("Application Startup");
            string path =Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"Chatserver");
            path = Path.Combine(path,"settings.xml");

            eventlog.WriteEntry("Reading config file");
            try
            {
                users = XML.Read(path);
            }
            catch (Exception ex)
            {
                eventlog.WriteEntry(string.Format("Reading config file failed with {0} and {1},{2},{3}", ex.Message, ex.InnerException, ex.Source, ex.StackTrace));

            }
            //Users read in. 0 users read in.????
            eventlog.WriteEntry(string.Format("Users read in. {0} users read in.",users.Count().ToString()));

        }
        //http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx
        public List<Record> HandleUserRequest()
        {
            lock (activeusers)
            {
                eventlog.WriteEntry("Active User List requested");
                return this.activeusers.ToList();
            }
        }
        public FileStore HandleFileRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (activeusers)
            {
                if (!activeusers.isLoggedIn(username)) return null;
            }
            lock (fileStore)//obtain lock on filestore before proceeding
            {
                switch (therequest.Type)
                {
                    case RequestType.GetFile:
                        //chatserver reads inthe filebytes before sending it
                        // it does not remove the entry from memory
                        //File reversed in chatserver.svc
                        eventlog.WriteEntry(string.Format("{0} has requested file {1}", username, therequest.GUID));
                            return new FileStore(fileStore.getByGUID(therequest.GUID));
                    //file
                    case RequestType.File:
                            string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Chatserver");
                            FilePost item = new FilePost();
                            item.GUID = Guid.NewGuid().ToString();
                            item.LocalFile = Path.Combine(path, therequest.FileName);
                            item.User = username;
                            item.Time = DateTime.Now;
                            fileStore.Add(item);
                            System.IO.File.WriteAllBytes(item.LocalFile, therequest.File);
                            Reply.SendNotificationFile(activeusers.getIP(therequest.Destination), activeusers.getPort(therequest.Destination), item.GUID);

                        eventlog.WriteEntry(string.Format("{0} has sent file {1}",username,item.GUID));
                        break;
                    case RequestType.FowardFile:
                        eventlog.WriteEntry(string.Format("{0} has forwarded file {1} to {2}", username, therequest.Destination));
                        Reply.SendNotificationFile(activeusers.getIP(therequest.Destination), activeusers.getPort(therequest.Destination), therequest.GUID);
                        return null;
                        break;
                }
            }
            return null;
        }
        public DMStore HandleDMStreamRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (activeusers)
            {
                if (!activeusers.isLoggedIn(username)) return null;
            }
            lock (DMstream)
            {
                eventlog.WriteEntry(string.Format("{0} has requested thier DM stream", username));
                switch (therequest.Type)
                {
                    case RequestType.GetDMStream:                       
                            return DMstream.getUserStream(username);
                        break;
                    case RequestType.GetDMStreamSince:
                        return DMstream.getUserStreamSince(username, therequest.Time);
                        break;
                }
            }
            return null;

        }
        public TimeStream<Post> HandleStreamRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (thestream)
            {
                eventlog.WriteEntry(string.Format("{0} has requested the stream", username));
                switch (therequest.Type)
                {
                    case RequestType.GetStream:
                        if (activeusers.isLoggedIn(username))
                        {
                            return thestream;
                        }
                        break;
                    case RequestType.GetStreamSince:
                        if (activeusers.isLoggedIn(username))
                        {
                            return thestream.getStreamSince(therequest.Time);
                        }
                        break;
                }

                return null;
            }
        }
        public bool HandleRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (activeusers)
            {
                if (therequest.Type == RequestType.Login)
                {
                    eventlog.WriteEntry(string.Format("{0} has started to login with password: {1}", username,therequest.Password)); // the password is purely for testing purposes
                    eventlog.WriteEntry(string.Format("{0} supplied password: {1} and the system has password {2}", username,therequest.Password, users.getPassword(username)));
                
                    if (users.Login(username, therequest.Password))// check that the username and password match
                    {
                        if (!activeusers.isLoggedIn(username))// check if they are already logged on
                        {
                            eventlog.WriteEntry(string.Format("{0} has successfully logged in.", username));
                            Reply.NotifyAllLogIn(activeusers, therequest.Username);// user will not have a TCP listner running yet so add after to avoid exception being thrown when trying to contact user
                            activeusers.Add(username, therequest.IP, therequest.Port, therequest.Status);
                            return true;
                        }
                        else
                        {
                            eventlog.WriteEntry(string.Format("Log in failed. {0} is already logged in", username));
                            return false;
                        }
                    }
                    else
                    {
                        eventlog.WriteEntry(string.Format("{0} failed to log in", username));
                        return false;
                    }
                }
                if (!activeusers.isLoggedIn(username)) return false;
                switch (therequest.Type)
                {
                    //message
                    case RequestType.Message:
                            eventlog.WriteEntry(string.Format("{0} has sent a message.", username));
                            Post thepost = new Post();
                            thepost.User = username;
                            thepost.IP = therequest.IP;
                            thepost.MessageContent = therequest.Mesage;
                            thepost.Time = DateTime.Now;
                            lock(thestream)
                            {
                                thestream.Add(thepost);
                            }
                        break;
                    //DM
                    case RequestType.DM:
                        eventlog.WriteEntry(string.Format("{0} has sent a message", username));
                        DMPost thedm = new DMPost();
                        thedm.Origin = username;
                        thedm.User = therequest.Destination;
                        thedm.MessageContent = therequest.Mesage;
                        thedm.IP = therequest.IP;
                        thedm.Time = therequest.Time;
                        lock (DMstream)
                        {
                            DMstream.Add(thedm);
                        }
                        if (activeusers.getStatus(therequest.Destination) == "Avaliable")
                        {
                            Reply.SendNotificationDM(activeusers.getIP(therequest.Destination), activeusers.getPort(therequest.Destination), username);
                        }
                        break;
                    //logoff
                    case RequestType.Logoff:
                        eventlog.WriteEntry(string.Format("{0} has logged off", username));
                        //update a central list/ ping connected clients to refresh
                        users.Update(username, activeusers.getIP(username),activeusers.getPort(username).ToString(), activeusers.getStatus(username));
                            activeusers.Remove(username);
                                Reply.NotifyAllLogOff(activeusers, username);
                        break;
                    //status update
                    case RequestType.Status:
                        eventlog.WriteEntry(string.Format("{0} has changed thier status to {1}", username,therequest.Status));
                            activeusers.UpdateStatus(username, therequest.Status);
                               Reply.NotifyAllStatus(activeusers, username);
                        break;
                }
                return true;
            }
        }
    }
}
